package cn.lili.controller.wxpay;

import cn.hutool.json.JSONUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.modules.payment.kit.core.kit.WxPayKit;
import cn.lili.modules.payment.kit.plugin.wechat.WechatV3Api;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.payment.WechatPaymentSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.jfinal.json.FastJson;
import com.jfinal.kit.HttpKit;
import com.jfinal.kit.JsonKit;
import com.jfinal.kit.Ret;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@SuppressWarnings("ALL")
@Slf4j
@Controller
@RequestMapping({"/buyer/payment/so/notify/WECHAT"})
public class NotifyController {
    @Autowired
    private SettingService settingService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @RequestMapping(value = "")
    @ResponseBody
    public void doNotify(HttpServletRequest request) throws Exception {
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String serialNo = request.getHeader("Wechatpay-Serial");
        String signature = request.getHeader("Wechatpay-Signature");

        log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
        //很重要
        String result = cn.lili.modules.payment.kit.core.kit.HttpKit.readData(request);
        log.info("miwen 微信支付通知密文 {}", result);

        WechatPaymentSetting setting = wechatPaymentSetting();

        //校验服务器端响应¬
        String plainText = WxPayKit.verifyNotify(setting.getMchId(), serialNo, result, signature, nonce, timestamp,
                setting.getApiKey3());

        log.info("mingwen 微信支付通知明文 {}", plainText);

        JSONObject jsonObject = JSON.parseObject(plainText);
        String tradeState = jsonObject.getString("trade_state");

        if ("SUCCESS".equals(tradeState)) {
            String outTradeNo = jsonObject.getString("out_trade_no");
            String transId = jsonObject.getString("transaction_id");
            String timeEnd = jsonObject.getString("success_time");
            Ret attach = FastJson.getJson().parse(new String(Base64.getDecoder().decode(jsonObject.getString("attach").getBytes(StandardCharsets.UTF_8))), Ret.class);
            String orderId = attach.getStr("orderId");
            String notifyUrl = stringRedisTemplate.opsForValue().get("wxpay:notify:" + outTradeNo);

            Ret savedData = FastJson.getJson().parse(plainText, Ret.class).set("notifyUrl", notifyUrl).set("orderId", orderId).set("isCombine", false);
            stringRedisTemplate.opsForValue().set("wx:pay:paid:" + attach.getStr("key"), savedData.toJson(), 30, TimeUnit.DAYS);
            stringRedisTemplate.opsForValue().set("wx:pay:paid:" + outTradeNo, savedData.toJson(), 30, TimeUnit.DAYS);
            stringRedisTemplate.opsForValue().set("wx:pay:paid:" + orderId, savedData.toJson(), 30, TimeUnit.DAYS);
            log.info("--回调地址：{}", notifyUrl);
            int total = jsonObject.getJSONObject("amount").getInteger("total");
            if (StringUtils.isEmpty(notifyUrl)) {
                log.info("--未配置回调链接");
            } else {
                try {
                    Map<String, String> notifyParams = new HashMap<>(4);
                    notifyParams.put("orderId", orderId);
                    notifyParams.put("amount", String.valueOf(Double.valueOf(total) * 0.01));
                    notifyParams.put("transId", transId);
                    notifyParams.put("timeEnd", timeEnd);
                    notifyParams.put("type", "order");

                    log.info("订单回调：{} 参数{}", notifyUrl, JsonKit.toJson(notifyParams));
                    String res = HttpKit.get(notifyUrl, notifyParams);
                    log.info("订单回调结果：{}", res);
                    JSONObject obj = JSONObject.parseObject(res);
                    if ("fail".equals(obj.getString("state"))) {
                        log.error("订单回调失败：{}", obj);
                        throw new RuntimeException();
                    }
                } catch (Exception e) {
                    log.error("订单回调失败", e);
                    throw new RuntimeException();
                }
            }
            log.info("分账开启状态：{}", setting.getIsProfitSharing());
            //分账

            String savedContent = stringRedisTemplate.opsForValue().get("wxpay:" + orderId);
            Ret orderInfo = FastJson.getJson().parse(savedContent, Ret.class);
            String receiverType = orderInfo.getStr("receiverType");
            String receiverAccount = orderInfo.getStr("receiverAccount");
            profitSharing(setting, jsonObject, outTradeNo, transId, orderId, total, receiverType, receiverAccount);
        }
    }

    //合单回调
    @RequestMapping(value = "combine")
    @ResponseBody
    public void combine(HttpServletRequest request) throws Exception {
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String serialNo = request.getHeader("Wechatpay-Serial");
        String signature = request.getHeader("Wechatpay-Signature");

        log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
        //很重要
        String result = cn.lili.modules.payment.kit.core.kit.HttpKit.readData(request);
        log.info("miwen 微信合单支付通知密文 {}", result);

        WechatPaymentSetting setting = wechatPaymentSetting();

        //校验服务器端响应¬
        String plainText = WxPayKit.verifyNotify(setting.getMchId(), serialNo, result, signature, nonce, timestamp,
                setting.getApiKey3());

        log.info("mingwen 微信合单支付通知明文 {}", plainText);

        JSONObject jsonObject = JSON.parseObject(plainText);
        JSONArray subOrders = jsonObject.getJSONArray("sub_orders");
        String notifyUrl = stringRedisTemplate.opsForValue().get("wxpay:notify:" + jsonObject.getString("combine_out_trade_no"));
        int successNum = 0;
        int total = 0;
        String orderId = "";
        List<String> timeEnds = new ArrayList<>();
        List<String> transIds = new ArrayList<>();

        for (JSONObject o : subOrders.stream().map(o -> (JSONObject) o).collect(Collectors.toList())) {

            String tradeState = o.getString("trade_state");
            if ("SUCCESS".equals(tradeState)) {
                successNum++;
                String outTradeNo = o.getString("out_trade_no");
                String transId = o.getString("transaction_id");
                String timeEnd = o.getString("success_time");
                Ret attach = FastJson.getJson().parse(new String(Base64.getDecoder().decode(o.getString("attach").getBytes(StandardCharsets.UTF_8))), Ret.class);
                orderId = attach.getStr("orderId");
                timeEnds.add(timeEnd);
                transIds.add(transId);
                Ret savedData = FastJson.getJson().parse(plainText, Ret.class).set("notifyUrl", notifyUrl).set("orderId", orderId).set("isCombine", true);
                stringRedisTemplate.opsForValue().set("wx:pay:paid:" + attach.getStr("key"), savedData.toJson(), 30, TimeUnit.DAYS);
                stringRedisTemplate.opsForValue().set("wx:pay:paid:" + outTradeNo, savedData.toJson(), 30, TimeUnit.DAYS);
                stringRedisTemplate.opsForValue().set("wx:pay:paid:" + orderId, savedData.toJson(), 30, TimeUnit.DAYS);

                total += o.getJSONObject("amount").getInteger("total_amount");

                log.info("分账开启状态：{}", setting.getIsProfitSharing());
                String finalOrderId = orderId;
                int finalTotal = o.getJSONObject("amount").getInteger("total_amount");

                String savedContent = stringRedisTemplate.opsForValue().get("wxpay:" + orderId);
                Ret orderInfo = FastJson.getJson().parse(savedContent, Ret.class);
                JSONArray orders = orderInfo.getAs("orders");
                JSONObject jjj = orders.stream().map(obj -> (JSONObject) obj).filter(obj -> outTradeNo.equals(obj.getString("outTradeNo"))).findFirst().orElse(null);
                if (jjj != null) {
                    String receiverType = jjj.getString("receiverType");
                    String receiverAccount = jjj.getString("receiverAccount");
                    profitSharing(setting, o, outTradeNo, transId, finalOrderId, finalTotal, receiverType, receiverAccount);
                } else {
                    profitSharing(setting, o, outTradeNo, transId, finalOrderId, finalTotal, null, null);
                }
            }
        }
        //全部订单完成
        if (successNum == subOrders.size()) {
            log.info("--回调地址：{}", notifyUrl);
            if (StringUtils.isEmpty(notifyUrl)) {
                log.info("--未配置回调链接");
            } else {
                try {
                    Map<String, String> notifyParams = new HashMap<>(4);
                    notifyParams.put("orderId", orderId);
                    notifyParams.put("amount", String.valueOf(Double.valueOf(total) * 0.01));
                    notifyParams.put("transId", StringUtils.join(transIds, ","));
                    notifyParams.put("timeEnd", StringUtils.join(timeEnds, ","));
                    notifyParams.put("type", "order");

                    log.info("订单回调：{} 参数{}", notifyUrl, JsonKit.toJson(notifyParams));
                    String res = HttpKit.get(notifyUrl, notifyParams);
                    log.info("订单回调结果：{}", res);
                    JSONObject obj = JSONObject.parseObject(res);
                    if ("fail".equals(obj.getString("state"))) {
                        log.error("订单回调失败：{}", obj);
                        throw new RuntimeException();
                    }
                } catch (Exception e) {
                    log.error("订单回调失败", e);
                    throw new RuntimeException();
                }
            }
        }
    }

    private void profitSharing(WechatPaymentSetting setting, JSONObject jsonObject, String outTradeNo, String transId, String orderId, int total, String receiverType, String receiverAccount) {
        //分账
        if ("1".equals(setting.getIsProfitSharing())) {
            log.info("添加分账任务，将在{}执行", DateFormatUtils.format(DateUtils.addSeconds(new Date(), 90), "yyyy-MM-dd HH:mm:ss"));
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    log.info("订单{} {} 开始处理分账", transId, orderId);
                    try {
                        //String savedContent = stringRedisTemplate.opsForValue().get("wxpay:" + orderId);
                        //Ret orderInfo = FastJson.getJson().parse(savedContent, Ret.class);
                        //String receiverType = orderInfo.getStr("receiverType");
                        //String receiverAccount = orderInfo.getStr("receiverAccount");

                        if (StringUtils.isNotEmpty(receiverType) && StringUtils.isNotEmpty(receiverAccount)) {
                            int unSplitAmount = Double.valueOf(total * 0.3).intValue();
                            if (unSplitAmount > 1) {
                                Ret ret = WechatV3Api.profitSharing(
                                        Ret.create()
                                                .set("appid", setting.getServiceAppId())
                                                .set("sub_mchid", jsonObject.getString("sub_mchid"))
                                                .set("transaction_id", transId)
                                                .set("out_order_no", outTradeNo)
                                                .set("receivers", Arrays.asList(
                                                        Ret.create("type", receiverType)
                                                                .set("receiver_account", receiverAccount)
                                                                .set("amount", unSplitAmount)
                                                                .set("description", "分给商户" + jsonObject.getString("sub_mchid"))
                                                ))
                                                .set("finish", true)
                                );
                                log.info("分账结果：{}", ret);
                            } else {
                                log.error("分账失败：分账金额小于最小值1分");
                                Ret ret = WechatV3Api.completeProfitsharing(jsonObject.getString("sub_mchid"), transId, outTradeNo, "完成分账");
                                log.info("解冻分账结果：{}", ret);
                            }
                        } else {
                            log.error("分账失败：未配置分账方");
                            Ret ret = WechatV3Api.completeProfitsharing(jsonObject.getString("sub_mchid"), transId, outTradeNo, "完成分账");
                            log.info("解冻分账结果：{}", ret);
                        }
                    } catch (Exception e) {
                        log.error("订单{} {} 分账失败", transId, orderId);
                        log.error("分账异常", e);
                    }
                }
            }, 90000);
        } else {
            log.info("未开启分账");
        }
    }

    @RequestMapping(value = "complaint")
    @ResponseBody
    public void complaint(HttpServletRequest request) throws Exception {
//        String timestamp = request.getHeader("Wechatpay-Timestamp");
//        String nonce = request.getHeader("Wechatpay-Nonce");
//        String serialNo = request.getHeader("Wechatpay-Serial");
//        String signature = request.getHeader("Wechatpay-Signature");
//
//        log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
//        //很重要
//        String result = cn.lili.modules.payment.kit.core.kit.HttpKit.readData(request);
//        log.info("miwen 微信投诉通知密文 {}", result);
//
//        WechatPaymentSetting setting = wechatPaymentSetting();
//
//
//        //校验服务器端响应¬
//        String plainText = WxPayKit.verifyNotify(setting.getMchId(), serialNo, result, signature, nonce, timestamp,
//                setting.getApiKey3());
//
////        log.info("mingwen 微信投诉通知明文 {}", plainText);
//
//        JSONObject jsonObject = JSON.parseObject(plainText);
//        log.info("\nmingwen 微信投诉通知明文\n{}", JSON.toJSONString(jsonObject, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue
//                , SerializerFeature.WriteDateUseDateFormat));
//        String actionType = jsonObject.getString("action_type");
//        if ("CREATE_COMPLAINT".equals(actionType) || "CONTINUE_COMPLAINT".equals(actionType)) {
//
//            String complaintId = jsonObject.getString("complaint_id");
//            Ret complaintDetail = WechatV3Api.getComplaintDetail(complaintId);
//            log.info("用户投诉{} 投诉详情：{} 投诉时间：{} 投诉次数：{}", complaintDetail.getStr("problem_description"), complaintDetail.getStr("complaint_detail"), complaintDetail.getStr("complaint_time"), complaintDetail.getStr("user_complaint_times"));
//            JSONArray orders = jsonObject.getJSONArray("complaint_order_info");
//
//            String paidData = null;
//            if (CollectionUtils.isNotEmpty(orders)) {
//                String out_trade_no = orders.getJSONObject(0).getString("out_trade_no");
//                String refOrderId = stringRedisTemplate.opsForValue().get("wxpay:idref:" + out_trade_no);
//                if (StringUtils.isNotEmpty(refOrderId)) {
//                    paidData = stringRedisTemplate.opsForValue().get("wx:pay:paid:" + refOrderId);
//                }
//            } else {
//                String outTradeNo = jsonObject.getString("out_trade_no");
//                paidData = stringRedisTemplate.opsForValue().get("wx:pay:paid:" + outTradeNo);
//
//
//            }
//            if (StringUtils.isNotEmpty(paidData)) {
//                Ret paid = FastJson.getJson().parse(paidData, Ret.class);
//                try {
//                    String notifyUrl = paid.getStr("notifyUrl");
//                    if (StringUtils.isNotEmpty(notifyUrl)) {
//                        Map<String, String> notifyParams = new HashMap<>(4);
//                        notifyParams.put("type", "complaint");
//                        notifyParams.put("attr", complaintDetail.toJson());
//                        notifyParams.put("complaintId", complaintId);
//                        notifyParams.put("orderId", paid.getStr("orderId"));
//
//                        log.info("订单投诉回调：{} 参数{}", notifyUrl, notifyParams);
//                        String res = HttpKit.get(notifyUrl, notifyParams);
//                        log.info("订单回调结果：{}", res);
//                        JSONObject o = JSONObject.parseObject(res);
//                        if ("fail".equals(o.getString("state"))) {
//                            log.error("订单投诉回调失败：{}", o);
//                            throw new RuntimeException();
//                        }
//                    } else {
//                        log.info("---订单投诉回调失败，未配置回调链接");
//                    }
//                } catch (Exception e) {
//                    log.error("订单投诉回调失败", e);
//                    throw new RuntimeException();
//                }
//            } else {
//                log.info("--投诉未找到支付信息 投诉详情：\n{}", JSON.toJSONString(complaintDetail, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue
//                        , SerializerFeature.WriteDateUseDateFormat));
//            }
//        }
    }

    @RequestMapping(value = "refund")
    @ResponseBody
    public void refund(HttpServletRequest request) throws Exception {
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String serialNo = request.getHeader("Wechatpay-Serial");
        String signature = request.getHeader("Wechatpay-Signature");

        log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
        //很重要
        String result = cn.lili.modules.payment.kit.core.kit.HttpKit.readData(request);
        log.info("miwen 微信退款通知密文 {}", result);
        JSONObject ciphertext = JSONObject.parseObject(result);
        WechatPaymentSetting setting = wechatPaymentSetting();


        //校验服务器端响应¬
        String plainText = WxPayKit.verifyNotify(setting.getMchId(), serialNo, result, signature, nonce, timestamp,
                setting.getApiKey3());

        log.info("mingwen 微信退款通知明文 {}", plainText);

        JSONObject jsonObject = JSON.parseObject(plainText);
        log.info("\n{}", JSON.toJSONString(jsonObject, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue
                , SerializerFeature.WriteDateUseDateFormat));

        String paidData = stringRedisTemplate.opsForValue().get("wx:pay:paid:" + jsonObject.getString("out_trade_no"));
        Ret paid = FastJson.getJson().parse(paidData, Ret.class);
        String notifyUrl = paid.getStr("notifyUrl");

        Map<String, String> notifyParams = new HashMap<>(4);
        notifyParams.put("type", "refund");
        notifyParams.put("attr", plainText);
        notifyParams.put("orderId", paid.getStr("orderId"));
        if (("REFUND.SUCCESS").equals(ciphertext.getString("event_type"))) {
            log.info("退款成功 {}", plainText);
            notifyParams.put("refundStatus", "2");
        } else {
            log.info("退款失败 {}", plainText);
            notifyParams.put("refundStatus", "3");
        }

        log.info("订单投诉回调：{} 参数{}", notifyUrl, notifyParams);
        String res = HttpKit.get(notifyUrl, notifyParams);
        log.info("订单回调结果：{}", res);
        JSONObject o = JSONObject.parseObject(res);
        if ("fail".equals(o.getString("state"))) {
            log.error("订单退款回调失败：{}", o);
            throw new RuntimeException();
        }
    }

    /**
     * 获取微信支付配置
     *
     * @return
     */
    private WechatPaymentSetting wechatPaymentSetting() {
        try {
            Setting systemSetting = settingService.get(SettingEnum.WECHAT_PAYMENT.name());
            WechatPaymentSetting wechatPaymentSetting = JSONUtil.toBean(systemSetting.getSettingValue(), WechatPaymentSetting.class);
            return wechatPaymentSetting;
        } catch (Exception e) {
            log.error("微信支付暂不支持", e);
            throw new ServiceException(ResultCode.PAY_NOT_SUPPORT);
        }
    }
}
